package com.sywl.ytcx.service.Impl;

import com.sywl.ytcx.config.RedisConstants;
import com.sywl.ytcx.entity.res.ResponseResult;
import com.sywl.ytcx.mapper.UserMapper;
import com.sywl.ytcx.service.CommunityActivityService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.time.LocalDate;
import java.time.YearMonth;
import java.time.ZoneId;
import java.util.*;

/**
 * @author cjx
 * @description: 社区活跃度服务实现类
 * @date 2025/8/1 10:04
 */
/**
 * 社区活跃度服务实现类，统一返回 ResponseResult
 */
@Service
public class CommunityActivityServiceImpl implements CommunityActivityService {

    private final StringRedisTemplate redisTemplate;
    private final UserMapper userMapper;

    @Autowired
    public CommunityActivityServiceImpl(StringRedisTemplate redisTemplate, UserMapper userMapper) {
        // 使用 Objects.requireNonNull 确保依赖不为 null
        this.redisTemplate = Objects.requireNonNull(redisTemplate, "redisTemplate 不能为 null");
        this.userMapper = Objects.requireNonNull(userMapper, "userMapper 不能为 null");
    }

    /**
     * 记录用户登录行为，使用 Redis HyperLogLog 数据结构统计唯一用户数
     *
     * @param account 用户账号
     * @return ResponseResult 响应结果
     */
    @Override
    public ResponseResult recordLogin(String account) {
        // 参数校验：账号不能为空
        if (account == null || account.isBlank()) {
            return new ResponseResult("CA400", "account 不能为空", null);
        }

        // 获取当前日期，构造 Redis key
        LocalDate today = LocalDate.now();
        String key = RedisConstants.HLL_KEY_PREFIX + today;

        // 将用户账号添加到当天的 HyperLogLog 中
        redisTemplate.opsForHyperLogLog().add(key, account);

        // 设置过期时间：从明天开始算起，保留到后天凌晨
        Duration ttl = Duration.between(
                today.atStartOfDay().plusDays(1),
                LocalDate.now().atStartOfDay().plusDays(2)
        ).abs();
        redisTemplate.expire(key, ttl);

        return new ResponseResult(200, "记录用户登录成功", null);
    }

    /**
     * 获取指定日期的日活跃用户数 (DAU)
     *
     * @param date 指定日期
     * @return ResponseResult 包含活跃用户数的结果
     */
    @Override
    public ResponseResult getDAU(LocalDate date) {
        // 参数校验：日期不能为空
        if (date == null) {
            return new ResponseResult("CA400", "date 不能为空", null);
        }

        // 不能查询未来日期的数据
        if (date.isAfter(LocalDate.now())) {
            return new ResponseResult("CA400", "不能查询未来日期的 DAU", null);
        }

        // 构造 Redis key 并获取统计值
        String key = RedisConstants.HLL_KEY_PREFIX + date;
        Long size = redisTemplate.opsForHyperLogLog().size(key);

        return new ResponseResult(200, "查询指定日期的日活跃用户数成功", size != null ? size : 0L);
    }

    /**
     * 获取指定日期范围内的周活跃用户数 (WAU)
     *
     * @param startOfWeek 周开始日期
     * @param endOfWeek   周结束日期
     * @return ResponseResult 包含活跃用户数的结果
     */
    @Override
    public ResponseResult getWAU(LocalDate startOfWeek, LocalDate endOfWeek) {
        // 参数校验：开始和结束日期都不能为空
        if (startOfWeek == null || endOfWeek == null) {
            return new ResponseResult("CA400", "开始或结束日期不能为空", null);
        }

        // 开始日期不能晚于结束日期
        if (startOfWeek.isAfter(endOfWeek)) {
            return new ResponseResult("CA400", "开始日期不能晚于结束日期", null);
        }

        // 不能查询未来日期范围的数据
        if (endOfWeek.isAfter(LocalDate.now())) {
            return new ResponseResult("CA400", "不能查询未来日期范围的 WAU", null);
        }

        // 收集日期范围内的所有 key
        List<byte[]> keys = new ArrayList<>();
        for (LocalDate d = startOfWeek; !d.isAfter(endOfWeek); d = d.plusDays(1)) {
            keys.add(redisTemplate.getStringSerializer().serialize(
                    RedisConstants.HLL_KEY_PREFIX + d
            ));
        }

        // 使用 Redis pfCount 命令合并统计多个 key 的唯一用户数
        Long count = redisTemplate.execute((RedisCallback<Long>) conn ->
                conn.pfCount(keys.toArray(new byte[0][]))
        );

        return new ResponseResult(200, "查询指定日期范围内的周活跃用户数成功", count != null ? count : 0L);
    }

    @Override
    public ResponseResult getMAU(YearMonth month) {
        // 参数校验：月份不能为空
        if (month == null) {
            return new ResponseResult("CA400", "month 不能为空", null);
        }

        // 使用明确时区（可改为 ZoneId.systemDefault() 或注入 Clock）
        ZoneId zone = ZoneId.of("Asia/Tokyo");
        LocalDate today = LocalDate.now(zone);
        YearMonth currentYm = YearMonth.from(today);

        // 禁止查询严格的未来月份（例如 2025-09、2025-10 等）
        if (month.isAfter(currentYm)) {
            return new ResponseResult("CA400", "不能查询未来月份的 MAU", null);
        }

        // 起始为当月第一天；截止为：如果是当前月则到今天，否则到当月月末
        LocalDate start = month.atDay(1);
        LocalDate end = month.equals(currentYm) ? today : month.atEndOfMonth();

        // 收集月份内（start .. end）所有日期对应的 key
        List<byte[]> keys = new ArrayList<>();
        for (LocalDate d = start; !d.isAfter(end); d = d.plusDays(1)) {
            keys.add(redisTemplate.getStringSerializer().serialize(
                    RedisConstants.HLL_KEY_PREFIX + d.toString()
            ));
        }

        // 使用 Redis pfCount 合并统计多个 key 的唯一用户数
        Long count = redisTemplate.execute((RedisCallback<Long>) conn ->
                conn.pfCount(keys.toArray(new byte[0][]))
        );

        return new ResponseResult(200, "查询指定月份的月活跃用户数成功", count != null ? count : 0L);
    }


    /**
     * 统计指定日期的新注册用户数
     *
     * @param date 指定日期
     * @return ResponseResult 包含新注册用户数的结果
     */
    @Override
    public ResponseResult countNewRegistrations(LocalDate date) {
        // 参数校验：日期不能为空
        if (date == null) {
            return new ResponseResult("CA400", "date 不能为空", null);
        }

        // 不能查询未来日期的数据
        if (date.isAfter(LocalDate.now())) {
            return new ResponseResult("CA400", "不能查询未来日期的新注册用户", null);
        }

        // 调用 UserMapper 查询数据库统计新注册用户数
        int count = userMapper.countByCreatedAt(date);

        return new ResponseResult(200, "查询指定日期的新注册用户数成功", count);
    }

    /**
     * 统计指定日期范围内的新注册用户数
     *
     * @param startDate 开始日期
     * @param endDate   结束日期
     * @return ResponseResult 包含新注册用户数的结果
     */
    @Override
    public ResponseResult countNewRegistrationsBetween(LocalDate startDate, LocalDate endDate) {
        // 参数校验：开始和结束日期都不能为空
        if (startDate == null || endDate == null) {
            return new ResponseResult("CA400", "开始或结束日期不能为空", null);
        }

        // 开始日期不能晚于结束日期
        if (startDate.isAfter(endDate)) {
            return new ResponseResult("CA400", "开始日期不能晚于结束日期", null);
        }

        // 不能查询未来日期范围的数据
        if (endDate.isAfter(LocalDate.now())) {
            return new ResponseResult("CA400", "不能查询未来日期范围的新注册用户", null);
        }

        // 调用 UserMapper 查询数据库统计日期范围内的新注册用户数
        int count = userMapper.countBetweenCreatedAt(startDate, endDate);

        return new ResponseResult(200, "查询指定日期范围内的新注册用户数成功", count);
    }
}
